package com.yzt.p_base.utils;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;

import com.yzt.p_base.BuildConfig;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * ThreadCenter 中提供了以下的任务执行：
 * 1. post类的顺序执行
 * 2. execute类的线程池中无须的任务执行
 *
 * 以上添加的任务 Runnable 统一通过 @see {@link #removeRunnable(Runnable)} 来进行任务的移除
 *
 * post类的顺序执行 任务提供了以下几种case：
 *   - postUiTask 提供了任务post到Ui线程中执行，postUiTask提供了多个可选参数
 *   - post到子线程中执行任务（postNormalThread、postBackgroundThread、postWorkThread）
 *     - 线程优先级 Normal > Background > Work
 *
 * execute类的线程池中无须的任务执行：
 *   - 如果需要在后台执行任务，并且不需要顺序执行，那边可以使用线程池来提供并发效率
 *
 * 关于clear：
 *   - @see {@link #clear(HandlerKeyable)} 该方法已经废弃
 *   - @see {@link #clearKeyAbleAllUiRunnable(HandlerKeyable)} 该方法是指清除HandlerKeyable对应Handler中的所有Runnable，并清除
 *   Handler的引用
 *
 */
public final class ThreadCenter {
    private static final String TAG = "ThreadCenter";

    public static final String NORMAL_THREAD_HANDLER = "MiniSdkNormalHandler";
    public static final String BACKGROUND_THREAD_HANDLER = "MiniSdkBackgroundHandler";
    public static final String WORK_THREAD_HANDLER = "MiniSdkWorkHandler";

    static volatile ITask sTaskExecute = new ThreadImpl();
    public static boolean sDebugEnv = false;

    /**
     * debug模式，此时会做任务耗时监控
     */
    public static void setDebugMode() {
        sDebugEnv = true;
    }

    /**
     * 在主线程（UI）执行任务
     * @param task 需要在UI线程执行的任务
     */
    public static void postUITask(Runnable task) {
        postUITask(task, 0);
    }

    /**
     * 默认HandlerLabel，仅供实在没有明确回收节点时使用
     * @param task task
     * @param delayMills 延时
     */
    public static void postUITask(Runnable task, long delayMills) {
        postUITask(task, delayMills, false);
    }

    /**
     * 在主线程（UI）执行任务
     * @param task task
     * @param delayMills 延时
     * @param high 是否高优先级
     */
    public static void postUITask(Runnable task, long delayMills, boolean high) {
        postUITask(null, task, delayMills, high);
    }

    /**
     * 在主线程（UI）执行任务
     * @param task task
     * @param high 是否高优先级
     */
    public static void postUITask(Runnable task, boolean high) {
        postUITask(task, 0, high);
    }

    /**
     * 在主线程（UI）执行任务
     *
     * @param keyAble 创建独立的handle
     * @param task 需要在UI线程执行的任务
     */
    public static void postUITask(HandlerKeyable keyAble, Runnable task) {
        postUITask(keyAble, task, 0);
    }

    /**
     * 在主线程（UI）执行任务
     * @param keyAble
     * @param task 需要在UI线程执行的任务
     * @param highPriority true 任务会立即执行，或者当前任务执行完后执行
     */
    public static void postUITask(HandlerKeyable keyAble, Runnable task, boolean highPriority) {
        postUITask(keyAble, task, 0, highPriority);
    }

    /**
     * 延时UI任务
     * @param keyAble Handler标签
     * @param task 需要在UI线程执行的任务
     * @param delayMills 延时
     */
    public static void postUITask(HandlerKeyable keyAble, Runnable task, long delayMills) {
        postUITask(keyAble, task, delayMills, false);
    }

    /**
     * real post ui task
     * @param keyAble key
     * @param task task
     * @param delayMills delay ms
     * @param high postAtFrontOfQueue
     */
    private static void postUITask(HandlerKeyable keyAble, Runnable task, long delayMills, boolean high) {
        sTaskExecute.postMainTask(keyAble, task, delayMills, high);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 正常优先级的线程执行，顺序执行
     * @param task 需要在后台Normal优先级的线程中执行的任务
     */
    public static void postNormalThread(Runnable task) {
        postNormalThread(task, 0);
    }

    /**
     * 正常优先级的线程执行，顺序执行
     * @param task 需要在后台Normal优先级的线程中执行的任务
     * @param delayMills 延迟执行任务的时长，毫秒
     */
    public static void postNormalThread(Runnable task, long delayMills) {
        sTaskExecute.postLogicTask(task,
                delayMills,
                false,
                NORMAL_THREAD_HANDLER,
                android.os.Process.THREAD_PRIORITY_DEFAULT);
    }

    /**
     * 后台线程执行，顺序执行
     * @param task 需要在后台Background优先级的线程中执行的任务
     */
    public static void postBackgroundThread(Runnable task) {
        postBackgroundThread(task, 0);
    }

    /**
     * 后台线程执行，顺序执行
     * @param task 需要在后台Background优先级的线程中执行的任务
     * @param delayMills 延迟执行任务的时长，毫秒
     */
    public static void postBackgroundThread(Runnable task, long delayMills) {
        sTaskExecute.postLogicTask(task,
                delayMills,
                false,
                BACKGROUND_THREAD_HANDLER,
                android.os.Process.THREAD_PRIORITY_BACKGROUND);
    }

    /**
     * 后台work线程执行，顺序执行
     * @param task 需要在后台Work优先级的线程中执行的任务
     */
    public static void postWorkThread(Runnable task) {
        postWorkThread(task, 0);
    }

    /**
     * 后台work线程执行，顺序执行
     * @param task 需要在后台Work优先级的线程中执行的任务
     * @param delayMills 延迟执行任务的时长，毫秒
     */
    public static void postWorkThread(Runnable task, long delayMills) {
        sTaskExecute.postLogicTask(task,
                delayMills,
                false,
                WORK_THREAD_HANDLER,
                (android.os.Process.THREAD_PRIORITY_DEFAULT + android.os.Process.THREAD_PRIORITY_BACKGROUND) / 2);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 线程池中执行任务，无法保证任务执行时许
     * @param task 执行的任务
     */
    public static void execute(Runnable task) {
        execute(task, 0);
    }

    /**
     * 线程池中执行任务，无法保证任务执行时许
     * @param task 执行的任务
     * @param delayMills 任务需要延迟多久
     */
    public static void execute(Runnable task, long delayMills) {
        execute(task, delayMills, false);
    }

    /**
     * 线程池中执行任务，无法保证任务执行时许
     * @param task 需要在线程池执行的任务
     * @param highPriority true 任务会立即执行，或者当前任务执行完后执行
     */
    public static void execute(Runnable task, boolean highPriority) {
        execute(task, 0, highPriority);
    }

    /**
     * 线程池中执行任务，无法保证任务执行时许
     * @param task 执行的任务
     * @param delayMills 任务需要延迟多久
     * @param highPriority 是否高优先级
     */
    public static void execute(Runnable task, long delayMills, boolean highPriority) {
        sTaskExecute.postLogicTask(task, delayMills, highPriority, null, 0);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 清理掉Ui线程中对应keyAble的map中对应Handler中的任务
     * @param label handlerKeyAble
     */
    public static void clearKeyAbleAllUiRunnable(HandlerKeyable label) {
        clear(label);
    }

    @Deprecated
    public static void clear(HandlerKeyable label) {
        sTaskExecute.clear(label);
    }

    /**
     * 在 @see ThreadCenterOrigin#ThreadImpl 实现中，该方法只能移除线程池中的task，优化方案稳定后，后续的 removeRunnable 重载方法将废弃删除
     * 在 @see #ThreadCenter#ThreadImpl 移除Handler，线程池，ui线程中未执行的任务
     * @param task 需要被移除的任务
     */
    public static void removeRunnable(Runnable task) {
        sTaskExecute.removeLogicTask(task, null);
    }

    /**
     * 移除ui线程中对应HandlerKeyable未执行的任务
     * @param task 需要被移除的任务
     */
    @Deprecated
    public static void removeRunnable(Runnable task, @NonNull HandlerKeyable keyAble) {
        sTaskExecute.removeUITask(keyAble, task);
    }

    /**
     * 移除Handler，线程池中未执行的任务
     * @param task 需要被移除的任务
     */
    @Deprecated
    public static void removeRunnable(Runnable task, String threadName) {
        sTaskExecute.removeLogicTask(task, threadName);
    }

    /**
     * 内部抽离的公共代码部分
     * @param r runnable
     * @param map map
     */
    private static void removeRunnable(Runnable r, final @NonNull Map<Runnable, List<InnerRealRunnable>> map) {
        List<InnerRealRunnable> realRunnableList = map.remove(r);
        if (realRunnableList != null) {
            for (InnerRealRunnable runnable : realRunnableList) {
                if (runnable.handler != null) {
                    runnable.handler.removeCallbacks(runnable);
                }
            }
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////

    public interface HandlerKeyable {
    }

    /**
     * 默认HandlerLabel，仅供实在没有明确回收节点时使用
     */
    public static HandlerKeyable sDefaultHandlerKey = new HandlerKeyable() {
    };

    /**
     * 提供单元测试获取Looper，外部无法获取
     * @param threadName 线程名称
     * @return looper
     */
    private static Looper getHandlerThreadLooper(String threadName) {
        return sTaskExecute.getHandlerThreadLooper(threadName);
    }

    /****************************************************************************************************************/

    private static class ThreadImpl implements ITask {

        private static final String TAG = "threadimpl_log";

        private final Object mLock = new Object();
        private final ConcurrentHashMap<Integer, Handler> uiHandlerMap = new ConcurrentHashMap<>();
        private final Handler taskScheduleHandler;
        private final ThreadPoolExecutor threadPoolExecutor;
        private final ConcurrentHashMap<Object, Runnable> delayHandlerForThreadPoolTaskMap = new ConcurrentHashMap<>();
        private final ConcurrentHashMap<String, CoreHandler> handlerThreadMap = new ConcurrentHashMap<>();

        private final ArrayList<CoreHandler> idleCoreHandlers = new ArrayList<>(8);
        private final Map<Runnable, List<InnerRealRunnable>> cacheUiRunnableMap = new ConcurrentHashMap<>();

        private volatile boolean shutdown = false;

        public ThreadImpl() {
            //延迟任务调度器
            HandlerThread taskScheduleHandlerThread = new HandlerThread("basetimer");
            taskScheduleHandlerThread.start();
            taskScheduleHandler = new Handler(taskScheduleHandlerThread.getLooper());

            //配置工作线程池数量，目前App主要是CPU密集型，线程数量不能太多
            int cores = Runtime.getRuntime().availableProcessors();
            int poolSize = 2;
            if (cores > 0) {
                poolSize = cores;
            }
            Log.v(TAG, "thread pool size " + poolSize);

            // 当corePoolSize和maximumPoolSize一样的话，keepAliveTime就是无效的设置
            // 为了让任务有机会更好的执行，在使用完核心线程时，希望能够扩容增加临时处理线程
            // 增加2个临时的扩容处理线程，线程的空闲存活时间是60s
            threadPoolExecutor = new ThreadPoolExecutor(
                    poolSize,
                    poolSize + 2,
                    60,
                    TimeUnit.SECONDS,
                    new PriorityBlockingQueue<Runnable>(30));

            checkIdleCoreHandler();
            Log.v(TAG, "thread create ok");
        }

        @Override
        public void shutdown() {
            shutdown = true;
        }

        /**
         * 判断当前是否设置了shutdown
         *
         * @return isShutdown
         */
        @Override
        public boolean isShutdown() {
            return shutdown;
        }

        @Override
        public void postMainTask(HandlerKeyable keyable, Runnable task, long delayMills, boolean high) {
            if (task == null) {
                return;
            }
            if (keyable == null) {
                keyable = ThreadCenter.sDefaultHandlerKey;
            }
            Handler handler = uiHandlerMap.get(keyable.hashCode());
            if (handler == null) {
                handler = new Handler(Looper.getMainLooper());
                uiHandlerMap.put(keyable.hashCode(), handler);
            }
            final InnerRealRunnable realRunnable = new InnerRealRunnable(task,
                    cacheUiRunnableMap,
                    handler,
                    delayMills,
                    "main");
            handleRunnableMap(task, realRunnable, cacheUiRunnableMap);
            if (delayMills > 0) {
                handler.postDelayed(realRunnable, delayMills);
            } else {
                if (high) {
                    handler.postAtFrontOfQueue(realRunnable);
                } else {
                    handler.post(realRunnable);
                }
            }
        }

        @Override
        public void postLogicTask(final Runnable task, long delayMills, boolean highPriority,
                String threadName,
                int threadPriority) {
            if (task == null) {
                return;
            }
            if (threadName == null) {
                if (delayMills > 0) {
                    //延迟后发送到线程池
                    final Runnable delayTask = new Runnable() {
                        @Override
                        public void run() {
                            // 如果执行到这处，刚好task被外部remove时，那么这个任务就不应该被执行
                            Runnable runnable = delayHandlerForThreadPoolTaskMap.remove(task);
                            if (runnable != null) {
                                final TaskProxy taskProxy = new TaskProxy(10, task);
                                threadPoolExecutor.execute(taskProxy);
                            }
                        }
                    };
                    delayHandlerForThreadPoolTaskMap.put(task, delayTask);
                    taskScheduleHandler.postDelayed(delayTask, delayMills);
                } else {
                    // 直接放入线程池队列的任务
                    threadPoolExecutor.execute(new TaskProxy(highPriority ? 0 : 10, task));
                }
            } else {
                synchronized (mLock) { // 添加独立threadName任务时，需要确保执行逻辑中的cache的handle是唯一的
                    CoreHandler singleHandler = null;
                    if (handlerThreadMap.containsKey(threadName)) {
                        singleHandler = handlerThreadMap.get(threadName);
                    }
                    if (singleHandler == null) {
                        singleHandler = new CoreHandler(threadName, threadPriority);
                        handlerThreadMap.put(threadName, singleHandler);
                        Log.v(TAG, "create new thread: " + threadName);
                    }

                    if (delayMills > 0) {
                        singleHandler.postDelayed(task, delayMills);
                    } else {
                        singleHandler.post(task, highPriority);
                    }
                }
            }
        }

        @Override
        public Looper getHandlerThreadLooper(String threadName) {
            if (StringUtils.equalsIgnoreCase(threadName, "basetimer")) {
                return taskScheduleHandler.getLooper();
            }
            CoreHandler coreHandler = handlerThreadMap.get(threadName);
            return coreHandler != null ? coreHandler.getLooper() : null;
        }

        /**
         * 清理掉Ui线程中对应keyAble的map中对应Handler中的任务
         * @see #clearKeyAbleAllUiRunnable(HandlerKeyable)
         * @param keyAble key
         */
        @Override
        public void clearKeyAbleAllUiRunnable(@NonNull HandlerKeyable keyAble) {
            clear(keyAble);
        }

        /**
         * 清理掉Ui线程中对应keyAble的map中对应Handler中的任务
         * @param keyAble
         */
        @Override
        @Deprecated
        public void clear(@NonNull HandlerKeyable keyAble) {
            Handler handler = uiHandlerMap.get(keyAble.hashCode());
            if (handler != null) {
                handler.removeCallbacksAndMessages(null);
                uiHandlerMap.remove(keyAble.hashCode());
            }
        }

        @Override
        public void removeUITask(HandlerKeyable label, Runnable task) {
            removeRunnable(task);
        }

        @Override
        public void removeLogicTask(Runnable task, String threadName) {
            removeRunnable(task);
        }

        private void removeRunnable(Runnable task) {
            if (task == null) {
                return;
            }
            /*
             * 对于remove task来说，这里面task只会存在其中一种任务类型中
             * 1. 该task是否是主线程handler中
             * 2. 先判断是否在delayTasks中，如果在，那么直接remove掉，方法结束
             * 3. task不在delayTasks中，那么尝试去线程池队列中查找，如果在，那么直接remove掉，方法结束
             * 4. 以上情况都不存在，那边可能是独立的Handler线程中的task任务，遍历Handler，查找出执行的Handler，
             *    如果在，那么直接remove掉，方法结束
             * */

            if (cacheUiRunnableMap.containsKey(task)) {
                ThreadCenter.removeRunnable(task, cacheUiRunnableMap);
                return;
            }

            // 先快速的判断remove task是 延迟任务还是 非延迟任务
            if (delayHandlerForThreadPoolTaskMap.containsKey(task)) {
                //可能是延时任务?
                Runnable delayTask = delayHandlerForThreadPoolTaskMap.get(task);
                taskScheduleHandler.removeCallbacks(delayTask);
                delayHandlerForThreadPoolTaskMap.remove(task);
                Log.w(TAG, "remove delay task in thread pool ok!");
                return;
            }

            //已经在线程池队列了？
            for (Runnable r : threadPoolExecutor.getQueue()) {
                if (((TaskProxy) r).task == task) {
                    threadPoolExecutor.getQueue().remove(r);
                    Log.w(TAG, "remove task in thread pool ok!");
                    return;
                }
            }

            for (CoreHandler coreHandler : handlerThreadMap.values()) {
                if (coreHandler != null && coreHandler.hasCallbacks(task)) {
                    coreHandler.removeTask(task);
                    return;
                }
            }

            Log.e(TAG, "remove task in thread pool fail, no such task");
        }

        // 空闲的HandlerThread需要被回收
        private void checkIdleCoreHandler() {
            taskScheduleHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    cleanIdleCoreHandler();
                    if (shutdown) {
                        if (threadPoolExecutor.getQueue().size() == 0) {
                            threadPoolExecutor.shutdown();
                        }
                        if (!threadPoolExecutor.isShutdown() || handlerThreadMap.size() > 0) {
                            taskScheduleHandler.postDelayed(this, CoreHandler.MAX_LIFE);
                        }
                    } else {
                        taskScheduleHandler.postDelayed(this, CoreHandler.MAX_LIFE);
                    }
                }
            }, CoreHandler.MAX_LIFE);
        }

        private void cleanIdleCoreHandler() {
            Log.i(TAG, "idle check begin...");
            idleCoreHandlers.clear();
            synchronized (mLock) { // 确保处于idle的handle 被移除后才释放锁
                Iterator<Map.Entry<String, CoreHandler>> iterator = handlerThreadMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, CoreHandler> entry = iterator.next();
                    if (entry.getValue().isIdle()) {
                        iterator.remove();
                        idleCoreHandlers.add(entry.getValue());
                        Log.e(TAG, "remove handler thread: " + entry.getKey());
                    }
                }
            }
            for (CoreHandler coreHandler : idleCoreHandlers) {
                if (coreHandler != null) {
                    coreHandler.quitThread();
                }
            }
            idleCoreHandlers.clear();
        }
    }

    private static class CoreHandler {

        static final int MAX_LIFE = 5 * 60 * 1000;
        long lastPost = 0;
        Handler handler;
        HandlerThread handlerThread;
        boolean needIdleCheck = true;
        Map<Runnable, List<InnerRealRunnable>> taskMap = new ConcurrentHashMap<>();
        private String threadName = "";

        public CoreHandler(String threadName, int threadPriority) {
            if (TextUtils.isEmpty(threadName)) {
                throw new RuntimeException("Handle ID empty!");
            }
            this.threadName = threadName;
            handlerThread = new HandlerThread(threadName, threadPriority);
            handlerThread.start();
            handler = new Handler(handlerThread.getLooper());
        }

        public void postDelayed(Runnable r, long mills) {
            lastPost = System.currentTimeMillis() + mills;
            final InnerRealRunnable realRunnable = new InnerRealRunnable(r, taskMap, handler, mills, threadName);
            handleRunnableMap(r, realRunnable, taskMap);
            handler.postDelayed(realRunnable, mills);
        }

        public void post(Runnable r, boolean highPriority) {
            lastPost = System.currentTimeMillis();
            final InnerRealRunnable realRunnable = new InnerRealRunnable(r, taskMap, handler, 0, threadName);
            handleRunnableMap(r, realRunnable, taskMap);
            if (highPriority) {
                handler.postAtFrontOfQueue(realRunnable);
            } else {
                handler.post(realRunnable);
            }
        }

        //如果是很长时间没有接受任务，就认为空闲
        public boolean isIdle() {
            return needIdleCheck && System.currentTimeMillis() - lastPost > MAX_LIFE;
        }

        public void quitThread() {
            if (handlerThread.isAlive()) {
                handlerThread.quit();
            }
        }

        public Looper getLooper() {
            return handler.getLooper();
        }

        public boolean hasCallbacks(Runnable callback) {
            return taskMap.containsKey(callback);
        }

        public void removeTask(Runnable r) {
            removeRunnable(r, taskMap);
        }
    }

    private static void handleRunnableMap(final Runnable r,
            final InnerRealRunnable realRunnable,
            final @NonNull Map<Runnable, List<InnerRealRunnable>> map) {
        List<InnerRealRunnable> realRunnableList;
        boolean isContainsKey = map.containsKey(r);
        synchronized (map) {
            if (!isContainsKey) {
                realRunnableList = new ArrayList<>(4);
            } else {
                realRunnableList = map.get(r);
            }
            if (realRunnableList != null) {
                realRunnableList.add(realRunnable);
            }
        }
        if (!isContainsKey) {
            map.put(r, realRunnableList);
        }
    }

    /**
     * 此处的目的是为了记录当前handle中执行的 runnable，方便业务进行查询管理 remove
     */
    private static class InnerRealRunnable implements Runnable {

        private final Runnable taskRunnable;
        private final Map<Runnable, List<InnerRealRunnable>> map;
        private final long delayMills;
        private long postRunnableTime = 0;
        private final String threadName;

        private final Handler handler;

        public InnerRealRunnable(Runnable r, Map<Runnable, List<InnerRealRunnable>> map,
                Handler handler,
                long delayMills,
                String threadName) {
            taskRunnable = r;
            this.handler = handler;
            this.map = map;
            this.delayMills = delayMills;
            this.threadName = threadName;
            if (BuildConfig.DEBUG) {
                postRunnableTime = System.currentTimeMillis();
            }
        }

        @Override
        public void run() {
            if (BuildConfig.DEBUG) {
                Log.e(TAG, "threadName-" + threadName + " post task execute interval = " + delayMills
                        + " use time=" + (System.currentTimeMillis() - postRunnableTime));
            }
            if (taskRunnable != null) {
                taskRunnable.run();
            }
            final List<InnerRealRunnable> list = map.get(taskRunnable);
            if (list != null) {
                if (list.size() <= 1) {
                    map.remove(taskRunnable);
                } else {
                    synchronized (map) {
                        list.remove(this);
                    }
                }
            }
        }
    }

    public static class TaskProxy implements Runnable, Comparable<TaskProxy> {

        final static int MAX_TIME = 5 * 1000;
        final static String TAG = "taskproxy_log";
        int priority;
        Runnable task;
        volatile StackTraceElement[] stackElements;

        public TaskProxy(int priority, Runnable task) {
            this.priority = priority;
            this.task = task;
        }

        @Override
        public void run() {
            long begin = System.currentTimeMillis();
            task.run();
            long consumed = System.currentTimeMillis() - begin;
            //Log.e(TAG, "task consumed: " + consumed + "ms");
            if (consumed > MAX_TIME) {
                showTaskSrc();
            }
        }

        @Override
        public int compareTo(TaskProxy another) {
            return priority - another.priority;
        }

        void showTaskSrc() {
            if (!ThreadCenter.sDebugEnv) {
                return;
            }
            if (stackElements == null) {
                Throwable ex = new Throwable();
                stackElements = ex.getStackTrace();
            }
            String msg = stackElements[3].getClassName() + "(line " + stackElements[3].getLineNumber() + "):"
                    + stackElements[3].getMethodName();
            Log.e(TAG, "one task has consumed too long time:\n" + msg);
        }
    }
}
